package com.example.leetcode.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntConsumer;

//class ZeroEvenOdd {
//    private int n;
//    Semaphore[] semaphores = new Semaphore[3];
//
//    public ZeroEvenOdd(int n) {
//        this.n = n;
//        semaphores[0] = new Semaphore(1);
//        semaphores[1] = new Semaphore(0);
//        semaphores[2] = new Semaphore(0);
//    }
//
//    // printNumber.accept(x) outputs "x", where x is an integer.
//    public void zero(IntConsumer printNumber) throws InterruptedException {
//        for (int i = 1; i <= n; i++) {
//            semaphores[0].acquire();
//            printNumber.accept(0);
//            if (i % 2 == 0) semaphores[1].release();
//            else semaphores[2].release();
//        }
//    }
//
//    public void even(IntConsumer printNumber) throws InterruptedException {
//        for (int i = 2; i <= n; i = i + 2) {
//            semaphores[1].acquire();
//            printNumber.accept(i);
//            semaphores[0].release();
//        }
//    }
//
//    public void odd(IntConsumer printNumber) throws InterruptedException {
//        for (int i = 1; i <= n; i = i + 2) {
//            semaphores[2].acquire();
//            printNumber.accept(i);
//            semaphores[0].release();
//        }
//    }
//}


class ZeroEvenOdd {
    private int n;
    volatile int state;
    ReentrantLock lock = new ReentrantLock();
    Condition zeroConditon = lock.newCondition();
    Condition evenCondition = lock.newCondition();
    Condition oddCondition = lock.newCondition();

    public ZeroEvenOdd(int n) {
        this.n = n;
        state = 0;
    }

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for (int i = 1; i <= n; i++) {
            lock.lock();
            while (state != 0) {
                zeroConditon.await();
            }
            printNumber.accept(0);
            if (i % 2 == 1) {
                state = 1;
                oddCondition.signal();
            } else {
                state = 2;
                evenCondition.signal();
            }
            lock.unlock();
        }
    }

    public void even(IntConsumer printNumber) throws InterruptedException {
        for (int i = 2; i <= n; i++) {
            if (i % 2 != 0) {
                continue;
            }
            lock.lock();
            while (state != 2) {
                evenCondition.await();
            }
            printNumber.accept(i);
            state = 0;
            zeroConditon.signal();
            lock.unlock();
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
        for (int i = 1; i <= n; i++) {
            if (i % 2 != 1) {
                continue;
            }
            lock.lock();
            while (state != 1) {
                oddCondition.await();
            }
            printNumber.accept(i);
            state = 0;
            zeroConditon.signal();
            lock.unlock();
        }
    }
}

public class leetcode1116 {
    public static void main(String[] args) {

        ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(5);
        new Thread(() -> {
            IntConsumer intConsumer = System.out::print;
            try {
                zeroEvenOdd.zero(intConsumer);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "A").start();

        new Thread(() -> {
            IntConsumer intConsumer = System.out::print;
            try {
                zeroEvenOdd.odd(intConsumer);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "B").start();

        new Thread(() -> {
            IntConsumer intConsumer = System.out::print;
            try {
                zeroEvenOdd.even(intConsumer);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "C").start();

    }
}
